using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;

namespace XDL.Framework.OpenSteer
{
    public class LQProximityDatabase : AbstractProximityDatabase
    {

        locationQueryDatabase lq;

        // constructor
        public LQProximityDatabase(Vector3 center, Vector3 dimensions, Vector3 divisions)
        {
            Vector3 halfsize = dimensions * 0.5f;
            Vector3 origin = center - halfsize;
            

            lq = new locationQueryDatabase(origin.X, origin.Y, origin.Z,
                                   dimensions.X, dimensions.Y, dimensions.Z,
                                   (int)System.Math.Round(divisions.X),
                                   (int)System.Math.Round(divisions.Y),
                                   (int)System.Math.Round(divisions.Z));
        }

        // destructor
        ~LQProximityDatabase()
        {
            //lqDeleteDatabase(lq);
            //lq = NULL;
        }

        // "token" to represent objects stored in the database
        public class tokenType : AbstractTokenForProximityDatabase
        {
            lqClientProxy proxy;
            locationQueryDatabase lq;

            // constructor
            public tokenType(Object parentObject, LQProximityDatabase lqsd)
            {
                proxy = new lqClientProxy(parentObject);// lqInitClientProxy(proxy, parentObject);
                lq = lqsd.lq;
            }

            #region AbstractTokenForProximityDatabase Members

            public bool Activated
            {
                get
                {
                    return proxy.node != null;                    
                }
                set
                {
                    if (value == false)
                        lq.lqAddToBin(proxy, lq.other);
                    else
                        lq.lqRemoveFromBin(proxy);
                }
            }

            // the client object calls this each time its position changes
            public void updateForNewPosition(Vector3 p)
            {
                lq.lqUpdateForNewLocation(proxy, p);
            }

            // find all neighbors within the given sphere (as center and radius)
            public void findNeighbors(Vector3 center, float radius, List<AbstractVehicle> results)
            {
                //lqMapOverAllObjectsInLocality(lq,

                List<lqClientProxy> tList = lq.getAllObjectsInLocality(center, radius);
                //tList.ForEach(delegate(lqClientProxy tProxy)
                foreach(lqClientProxy tProxy in tList)
                {
                    results.Add((AbstractVehicle)tProxy.clientObject);
                }
            }
            #endregion

            // called by LQ for each clientObject in the specified neighborhood:
            // push that clientObject onto the ContentType vector in void*
            // clientQueryState
            // (parameter names commented out to prevent compiler warning from "-W")


            /*
            static void perNeighborCallBackFunction(void* clientObject,
                                                      float distanceSquared,
                                                      void* clientQueryState)
            {
                typedef std::vector<ContentType> ctv;
                ctv& results = *((ctv*) clientQueryState);
                results.push_back ((ContentType) clientObject);
            }
            */
        };


        // allocate a token to represent a given client object in this database
        public AbstractTokenForProximityDatabase allocateToken(AbstractVehicle parentObject)
        {
            return new tokenType(parentObject, this);
        }

        // count the number of tokens currently in the database
        public int getPopulation()
        {
            //int count = 0;
            //lqMapOverAllObjects(lq, counterCallBackFunction, &count);

            int count = lq.getAllObjects().Count;

            return count;
        }

        public AbstractVehicle getNearestVehicle(Vector3 position, float radius)
        {
            lqClientProxy tProxy = lq.lqFindNearestNeighborWithinRadius(position, radius, null);
            AbstractVehicle tVehicle = null;
            if (tProxy != null)
            {
                tVehicle = (AbstractVehicle)tProxy.clientObject;
            }
            return tVehicle;
        }

        public Vector3 getMostPopulatedBinCenter() 
        {
            return lq.getMostPopulatedBinCenter();
        } 


        /*
        // (parameter names commented out to prevent compiler warning from "-W")
        static void counterCallBackFunction(void* clientObject,
                                              float distanceSquared,
                                              void* clientQueryState)
        {
            int& counter = *(int*)clientQueryState;
            counter++;
        }
        */

    }
}
